#include "bsp.h"
#include "shell.h"

#define SHELL_BUFF_LEN  512
static Shell _shell;
static char _shellBuffer[SHELL_BUFF_LEN];

#define UART_RX_BUFF_LEN   100
static queue_t _uart_rx_queue;
static queue_elem_t _uart_rx_buff[UART_RX_BUFF_LEN];

/**
 * @brief shell写数据函数原型
 *
 * @param data 需写的字符数据
 * @param len 需要写入的字符数
 *
 * @return unsigned short 实际写入的字符数量
 */
static signed short shellWrite(char *data, unsigned short len)
{
    unsigned short i;
    
    for (i = 0; i < len; i++)
    {
        /* 采用阻塞方式发送每个字符,等待数据发送完毕 */
        usart_data_transmit(USART0, (uint8_t)data[i]);
        while (RESET == usart_flag_get(USART0, USART_FLAG_TBE))
        {}
    }

    return len;
}

/***************************************************************************************************
 * @fn      config_uart_nvic
 *
 * @brief   配置串口硬件中断
 *
 * @param   无
 *
 * @return  无
 */
static void config_uart_nvic(void)
{
    nvic_irq_enable(USART0_IRQn, 1, 0);  /* 使能串口中断，设置优先级 */
}

/***************************************************************************************************
 * @fn      uart_init_hard
 *
 * @brief   配置串口的硬件参数（波特率，数据位，停止位，起始位，校验位，中断使能）
 *
 * @param   无
 *
 * @return  无
 */
static void uart_init_hard(void)
{
    /* 串口1 TX = PA9   RX = PA10 或 TX = PB6   RX = PB7*/
    rcu_periph_clock_enable(RCU_GPIOA);  /* 使能GPIO时钟 */
    rcu_periph_clock_enable(RCU_USART0); /* 使能串口时钟 */

    gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_9);
    gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_10);
    
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);

    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
    
    /* 配置USART的参数 */
    usart_deinit(USART0);  /* RCU配置恢复默认值 */
    usart_baudrate_set(USART0, 115200);  /* 设置波特率 */
    usart_stop_bit_set(USART0, USART_STB_1BIT);  /* 设置停止位 */
    usart_word_length_set(USART0, USART_WL_8BIT);  /* 设置数据字长度 */
    usart_parity_config(USART0, USART_PM_NONE);  /* 设置奇偶校验位 */
    usart_receive_config(USART0, USART_RECEIVE_ENABLE);  /* 使能接收 */
    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);  /* 使能发送 */

    usart_interrupt_enable(USART0, USART_INT_RBNE);  /* 使能接收缓冲区非空中断 */

    usart_enable(USART0);  /* 使能串口 */

}

/***************************************************************************************************
 * @fn      bsp_init_print
 *
 * @brief   初始化串口硬件，并对全局变量赋初值
 *
 * @param   无
 *
 * @return  无
 */
void bsp_init_print(void)
{
    queue_init(&_uart_rx_queue, _uart_rx_buff, UART_RX_BUFF_LEN);
    
    uart_init_hard();  /* 配置串口的硬件参数(波特率等) */

    config_uart_nvic();  /* 配置串口中断 */
    
    _shell.write = shellWrite;
    shellInit(&_shell, _shellBuffer, SHELL_BUFF_LEN);
}

/***************************************************************************************************
 * @fn      fputc
 *
 * @brief   重定义putc函数，这样可以使用printf函数从串口1打印输出
 *
 * @param   无
 *
 * @return  无
 */
int fputc(int ch, FILE *f)
{

    /* 采用阻塞方式发送每个字符,等待数据发送完毕 */
    usart_data_transmit(USART0, (uint8_t)ch);
    while (RESET == usart_flag_get(USART0, USART_FLAG_TBE))
    {}

    return ch;
}

/***************************************************************************************************
 * @fn      fgetc
 *
 * @brief   重定义getc函数，这样可以使用getchar函数从串口1输入数据
 *
 * @param   无
 *
 * @return  无
 */
int fgetc(FILE *f)
{

    /* 等待串口1输入数据 */
    while (usart_flag_get(USART0, USART_FLAG_RBNE) == RESET);

    return (int)usart_data_receive(USART0);
}

/***************************************************************************************************
 * @fn      USART0_IRQHandler
 *
 * @brief   串口中断服务程序
 *
 * @param   无
 *
 * @return  无
 */
void USART0_IRQHandler(void)
{
    uint8_t ch;

    /* 处理接收中断  */
    if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE) != RESET)
    {
        usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);

        /* 从串口接收数据寄存器读取数据存放到接收FIFO */
        ch = usart_data_receive(USART0);
        
        queue_enter(&_uart_rx_queue, ch);
        
        // shellHandler(&_shell, ch);
    }

    /* 溢出错误标志为1 */
    if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_ERR_ORERR) == SET)       
    {                                                         
        usart_interrupt_flag_clear(USART0, USART_INT_FLAG_ERR_ORERR);  /* 清除溢出错误标志 */
        usart_data_receive(USART0);  /* 读取USART_DATA */ 
    }
}

void uart_shell_process(void)
{
    uint8_t ch;
    
    if (queue_delete(&_uart_rx_queue, &ch))
    {
        shellHandler(&_shell, ch);
    }
}

